/**
 * 开发环境ssr服务端启动入口文件
 **/
const express = require('express')
const app = express()
const path = require("path")
const webpack = require('webpack')
const MemoryFS = require('memory-fs')
const { createBundleRenderer } = require('vue-server-renderer')
const getContext = require('./utils/getContext')

const webpackDevMiddleware = require('webpack-dev-middleware')
const webpackHotMiddleware = require('webpack-hot-middleware')

const serverConfing = require('../config/webpack.server.config')
const clientConfing = require('../config/webpack.client.config')


//客户端使用热更新
clientConfing.entry.client = ['webpack-hot-middleware/client', clientConfing.entry.client]
clientConfing.output.filename = '[name].js'
clientConfing.plugins.push(
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoEmitOnErrorsPlugin()
)
//构建客户端代码，并监控文件变化实时编译
const clientCompiler = webpack(clientConfing)
const devMiddleware = webpackDevMiddleware(clientCompiler, {
  publicPath: clientConfing.output.publicPath,
  noInfo: true,
  stats: "errors-only"
})
app.use('/static', express.static(path.resolve(__dirname, '../static')))
//添加中间件
app.use(devMiddleware)
app.use( webpackHotMiddleware(clientCompiler) )


//构建服务端代码，写入内存
const serverCompiler = webpack(serverConfing)
const mfs = serverCompiler.outputFileSystem = new MemoryFS()


let bundleServer

//监控文件变化，获取最新vue-ssr-server-bundle.json
serverCompiler.watch({}, (err, stats) => {
  if (err) throw err
  stats = stats.toJson()
  stats.errors.forEach( error => console.error(error) )
  stats.warnings.forEach( warn => console.warn(warn) )

  bundleServer = JSON.parse( mfs.readFileSync(path.join(serverConfing.output.path, 'vue-ssr-server-bundle.json'), 'utf-8') )
  console.log('New bundleServer generated.')
})

//处理页面请求
const handleRequest = async res => {
  if (!bundleServer) {
    res.status(200).end('please wait for webpack build to complete')
    return
  }

  //从内存中读取页面模板文件
  const template = mfs.readFileSync(path.join(serverConfing.output.path, 'template.ssr.html'), 'utf-8')
  //从devMiddleware文件系统中读取vue-ssr-client-manifest.json
  const clientManifestResp = devMiddleware.fileSystem.readFileSync(path.join(clientConfing.output.path, 'vue-ssr-client-manifest.json'), 'utf-8')
  const clientManifest = JSON.parse(clientManifestResp)
  //构建renderer对象
  const renderer = createBundleRenderer(bundleServer, {
    runInNewContext: false,
    template,
    clientManifest
  })

  return renderer
}

//将vue实例渲染为字符串
const renderToString = (context, renderer) => {
  return new Promise( (resolve, reject) => {
    renderer.renderToString(context, (err, html) => {
      err ? reject(err) : resolve(html)
    })
  })
}

//监听所有路由
app.get('*', async (req, res) => {
  // let token
  // try {
  //   token = req.headers.cookie.split("token=")[1].split(';')[0]
  // } catch(e) {}
  const context = getContext(req)
  context.url = req.url
  // context.token = token
  try {
    const renderer = await handleRequest(res)
    const html = await renderToString(context, renderer)
    res.status(200).end(html)
  } catch(e) {
    console.error(e)
    res.status(404).end("error!")
  }
})

let port = 8010
app.listen(port, () => {
  console.log(`已监听 localhost:${port}, 正在构建项目请稍后...`)
})